package com.lframework.jh.push.api.controller;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.lframework.jh.push.api.bo.PushSmsBo;
import com.lframework.jh.push.api.config.properties.MockProperties;
import com.lframework.jh.push.api.dto.QuerySmsBatchDetailDto;
import com.lframework.jh.push.api.utils.SmsChannelUtil;
import com.lframework.jh.push.api.vo.CreatePushOrderVo;
import com.lframework.jh.push.api.vo.QueryPushOrderVo;
import com.lframework.jh.push.core.annotations.OpenApi;
import com.lframework.jh.push.core.components.mq.MqProducer;
import com.lframework.jh.push.core.components.resp.InvokeResult;
import com.lframework.jh.push.core.components.resp.InvokeResultBuilder;
import com.lframework.jh.push.core.constants.MqConstants;
import com.lframework.jh.push.core.constants.PatternPool;
import com.lframework.jh.push.core.controller.DefaultBaseController;
import com.lframework.jh.push.core.exceptions.ClientException;
import com.lframework.jh.push.core.exceptions.impl.DefaultClientException;
import com.lframework.jh.push.core.exceptions.impl.DefaultSysException;
import com.lframework.jh.push.core.exceptions.impl.InputErrorException;
import com.lframework.jh.push.core.utils.ApplicationUtil;
import com.lframework.jh.push.core.utils.IdUtil;
import com.lframework.jh.push.core.utils.JsonUtil;
import com.lframework.jh.push.core.utils.RegUtil;
import com.lframework.jh.push.core.utils.StringUtil;
import com.lframework.jh.push.core.vo.OpenApiReqVo;
import com.lframework.jh.push.plugin.core.EmptySmsPushChannelParams;
import com.lframework.jh.push.plugin.core.SmsPushChannel;
import com.lframework.jh.push.plugin.core.SmsPushChannelParams;
import com.lframework.jh.push.plugin.core.SmsPushDetailResp;
import com.lframework.jh.push.plugin.core.SmsPushResp;
import com.lframework.jh.push.plugin.core.dto.QuerySmsPushResultDetailDto;
import com.lframework.jh.push.plugin.core.dto.QuerySmsPushResultPageDto;
import com.lframework.jh.push.plugin.core.enums.SmsQueryType;
import com.lframework.jh.push.service.entity.App;
import com.lframework.jh.push.service.entity.AppChannel;
import com.lframework.jh.push.service.entity.Channel;
import com.lframework.jh.push.service.entity.Mch;
import com.lframework.jh.push.service.entity.PushOrder;
import com.lframework.jh.push.service.entity.SmsBatchDetail;
import com.lframework.jh.push.service.enums.PushOrderStatus;
import com.lframework.jh.push.service.enums.PushOrderType;
import com.lframework.jh.push.service.enums.SmsBatchDetailStatus;
import com.lframework.jh.push.service.service.AppChannelService;
import com.lframework.jh.push.service.service.AppService;
import com.lframework.jh.push.service.service.ChannelService;
import com.lframework.jh.push.service.service.MchService;
import com.lframework.jh.push.service.service.PushOrderService;
import com.lframework.jh.push.service.service.SmsBatchDetailService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 开放Api
 *
 * @author zmj
 */
@Slf4j
@Api(tags = "开放接口-短信推送")
@Validated
@RestController
@RequestMapping("/sms")
public class SmsPushController extends DefaultBaseController {

    @Autowired
    private AppService appService;

    @Autowired
    private MchService mchService;

    @Autowired
    private ChannelService channelService;

    @Autowired
    private PushOrderService pushOrderService;

    @Autowired
    private AppChannelService appChannelService;

    @Autowired
    private MqProducer mqProducer;

    @Autowired
    private MockProperties mockProperties;

    @Autowired
    private SmsBatchDetailService smsBatchDetailService;

    @ApiOperation("创建短信推送单")
    @OpenApi(sign = true)
    @PostMapping("/push")
    public InvokeResult<PushSmsBo> push(@Valid @RequestBody OpenApiReqVo req) throws Exception {
        log.info("接收创建推送单请求，vo {}", req);

        CreatePushOrderVo vo = null;
        try {
            vo = JsonUtil.parseObject(req.getParams(), CreatePushOrderVo.class);
            vo.setMchId(req.getMchId());
        } catch (Exception e) {
            throw new InputErrorException("请求报文不合法！");
        }

        ApplicationUtil.validate(vo);

        PushOrder pushOrder = null;
        try {
            SmsPushChannel pushChannel = null;
            try {
                pushChannel = SmsChannelUtil.getByChannel(vo.getChannel());
            } catch (Exception e) {
                throw new DefaultClientException("渠道：" + vo.getChannel() + "不存在！");
            }

            // 校验参数
            log.info("开始校验传入参数 channel {}", vo.getChannel());
            pushOrder = this.validate(vo, pushChannel);

            String batchNo = IdUtil.getBatchNo();
            pushOrder.setBatchNo(batchNo);
            // 保存推送单
            pushOrderService.save(pushOrder);
            log.info("创建推送单成功，pushOrder {}", pushOrder);

            Integer mchId = pushOrder.getMchId();
            String[] phoneNumbers = pushOrder.getPhoneNumbers().split(",");
            List<SmsBatchDetail> smsBatchDetails = Arrays.stream(phoneNumbers).map(t -> {
                SmsBatchDetail record = new SmsBatchDetail();
                record.setId(IdUtil.getId());
                record.setBatchNo(batchNo);
                record.setMchId(mchId);
                record.setPhoneNumber(t);
                record.setStatus(SmsBatchDetailStatus.CREATED);

                return record;
            }).collect(Collectors.toList());

            Class<? extends SmsPushChannelParams> paramsClazz = pushChannel.getClazz();
            if (paramsClazz == null) {
                paramsClazz = EmptySmsPushChannelParams.class;
            }
            log.info("获取到渠道参数class {}", paramsClazz.getName());
            SmsPushChannelParams params = JsonUtil.parseObject(pushOrder.getChannelParams(),
                paramsClazz);
            params.setPhoneNumbers(pushOrder.getPhoneNumbers());
            params.setExtra(vo.getExtra());
            params.setOrderId(pushOrder.getId());

            log.info("解析渠道参数 {}", params);

            log.info("开始发起推送");
            SmsPushResp resp = pushChannel.push(params);
            log.info("发起推送响应参数 {}", resp);

            if (resp.getDetails().stream().noneMatch(t -> t.getChannelFlag() == 1)) {
                throw new DefaultClientException(resp.getDetails().get(0).getErrorMsg());
            }

            for (SmsBatchDetail smsBatchDetail : smsBatchDetails) {
                SmsPushDetailResp detailResp = resp.getDetails().stream().filter(
                        t -> t.getPhoneNumber().equalsIgnoreCase(smsBatchDetail.getPhoneNumber()))
                    .findFirst().orElse(null);
                if (detailResp == null) {
                    continue;
                }
                smsBatchDetail.setBizId(detailResp.getBizId());
                if (detailResp.getChannelFlag() == 0) {
                    smsBatchDetail.setStatus(SmsBatchDetailStatus.FAIL);
                    smsBatchDetail.setErrorMsg(detailResp.getErrorMsg());
                }
            }

            smsBatchDetailService.saveBatch(smsBatchDetails);

            PushOrder record = new PushOrder();
            record.setId(pushOrder.getId());
            record.setChannelResp(JsonUtil.toJsonString(resp));
            record.setBizId(resp.getBizId());
            record.setStatus(PushOrderStatus.PUSHING);
            pushOrderService.updateById(record);

            log.info("pushOrderId {} 状态变更为 {} pushOrder {}", pushOrder.getId(),
                PushOrderStatus.PUSHING.getDesc(), pushOrder);

            QuerySmsBatchDetailDto querySmsBatchDetailDto = new QuerySmsBatchDetailDto();
            querySmsBatchDetailDto.setOrderId(pushOrder.getId());
            querySmsBatchDetailDto.setPhoneNumbers(pushOrder.getPhoneNumbers());
            querySmsBatchDetailDto.setBatchNo(pushOrder.getBatchNo());
            querySmsBatchDetailDto.setPushTime(pushOrder.getCreateTime());
            querySmsBatchDetailDto.setChannel(pushOrder.getChannel());
            querySmsBatchDetailDto.setBizId(record.getBizId());
            querySmsBatchDetailDto.setParams(params);
            querySmsBatchDetailDto.setQueryType(pushChannel.getQueryType());
            this.doQueryBatchDetail(querySmsBatchDetailDto, smsBatchDetails);
        } catch (Exception e) {
            if (e instanceof ClientException) {
                log.debug(e.getMessage(), e);
            } else {
                log.error(e.getMessage(), e);
            }

            if (pushOrder != null) {
                PushOrder record = new PushOrder();
                record.setId(pushOrder.getId());
                record.setStatus(PushOrderStatus.FAIL);
                record.setFailureTime(LocalDateTime.now());
                record.setErrorMsg(e.getMessage());
                pushOrderService.updateById(record);
                log.info("推送单失败，状态变更为 {} pushOrder {}", PushOrderStatus.FAIL.getDesc(),
                    pushOrder);

                smsBatchDetailService.updateFailByBatchNo(pushOrder.getBatchNo(), e.getMessage());
            } else {
                throw e;
            }
        }

        pushOrder = pushOrderService.getById(pushOrder.getId());

        return InvokeResultBuilder.success(new PushSmsBo(pushOrder));
    }

    @ApiOperation("查询短信推送单")
    @OpenApi(sign = true)
    @PostMapping("/query")
    public InvokeResult<PushSmsBo> query(@Valid @RequestBody OpenApiReqVo req) throws Exception {
        log.info("接收查询推送单请求，vo {}", req);

        QueryPushOrderVo vo = null;
        try {
            vo = JsonUtil.parseObject(req.getParams(), QueryPushOrderVo.class);
            vo.setMchId(req.getMchId());
        } catch (Exception e) {
            throw new InputErrorException("请求报文不合法！");
        }

        ApplicationUtil.validate(vo);

        if (StringUtil.isBlank(vo.getOrderId()) && StringUtil.isBlank(vo.getOuterNo())) {
            throw new InputErrorException("orderId和outerNo不能全部为空！");
        }

        PushOrder pushOrder = null;
        if (StringUtil.isNotBlank(vo.getOrderId())) {
            Wrapper<PushOrder> queryWrapper = Wrappers.lambdaQuery(PushOrder.class)
                .eq(PushOrder::getId, vo.getOrderId())
                .eq(PushOrder::getMchId, vo.getMchId())
                .eq(PushOrder::getAppId, vo.getAppId());
            pushOrder = pushOrderService.getOne(queryWrapper);
        } else {
            Wrapper<PushOrder> queryWrapper = Wrappers.lambdaQuery(PushOrder.class)
                .eq(PushOrder::getAppId, vo.getAppId())
                .eq(PushOrder::getMchId, vo.getMchId())
                .eq(PushOrder::getOuterNo, vo.getOuterNo());
            pushOrder = pushOrderService.getOne(queryWrapper);
        }

        if (pushOrder == null) {
            throw new DefaultClientException("推送单不存在！");
        }

        return InvokeResultBuilder.success(new PushSmsBo(pushOrder));
    }

    private PushOrder validate(CreatePushOrderVo vo, SmsPushChannel pushChannel) {
        Mch mch = mchService.findById(vo.getMchId());
        if (mch == null) {
            throw new InputErrorException("商户ID不存在！");
        }

        if (!mch.getAvailable()) {
            throw new InputErrorException("商户已停用，无法创建推送单！");
        }

        App app = appService.findById(vo.getAppId());
        if (app == null) {
            throw new InputErrorException("应用ID不存在！");
        }

        if (!app.getAvailable()) {
            throw new InputErrorException("应用已停用，无法创建推送单！");
        }

        Channel channel = channelService.findByCode(vo.getChannel());
        if (channel == null) {
            throw new InputErrorException("渠道不存在！");
        }

        if (!channel.getAvailable()) {
            throw new InputErrorException("渠道已停用，无法创建推送单！");
        }

        // 校验手机号
        String[] phoneNumbers = vo.getPhoneNumbers().split(",");
        for (String phoneNumber : phoneNumbers) {
            if (!RegUtil.isMatch(PatternPool.PATTERN_CN_TEL, phoneNumber)) {
                throw new InputErrorException("手机号：" + phoneNumber + "有误，请检查！");
            }
        }

        if (phoneNumbers.length > pushChannel.allowSmsCount()) {
            throw new InputErrorException("单次最多支持" + pushChannel.allowSmsCount() + "个号码！");
        }

        if (Arrays.stream(phoneNumbers).distinct().count() != phoneNumbers.length) {
            throw new InputErrorException("发现重复的手机号码！");
        }

        if (!RegUtil.isMatch(PatternPool.PATTERN_HTTP_URL, vo.getNotifyUrl())) {
            throw new InputErrorException("通知URL格式有误！");
        }

        Wrapper<PushOrder> checkOrderWrapper = Wrappers.lambdaQuery(PushOrder.class)
            .eq(PushOrder::getAppId, app.getId())
            .eq(PushOrder::getOuterNo, vo.getOuterNo());
        if (pushOrderService.count(checkOrderWrapper) > 0) {
            throw new InputErrorException("外部单号已存在，请保证外部单号唯一！");
        }

        AppChannel appChannel = appChannelService.findByAppIdAndChannelId(app.getId(),
            channel.getId());
        if (appChannel == null) {
            throw new InputErrorException("应用尚未配置渠道，无法创建推送单！");
        }

        if (!appChannel.getAvailable()) {
            throw new InputErrorException("应用的渠道尚未开通，无法创建推送单！");
        }

        pushChannel.validExtra(vo.getExtra());

        PushOrder pushOrder = new PushOrder();
        pushOrder.setId(IdUtil.getOrderId());
        pushOrder.setMchId(mch.getId());
        pushOrder.setAppId(app.getId());
        pushOrder.setChannelId(channel.getId());
        pushOrder.setChannel(channel.getCode());
        pushOrder.setPhoneNumbers(vo.getPhoneNumbers());
        pushOrder.setOuterNo(vo.getOuterNo());
        pushOrder.setChannelParams(appChannel.getParams());
        pushOrder.setNotifyUrl(
            (mockProperties.getEnabled() && StringUtil.isNotBlank(mockProperties.getSmsNotifyUrl()))
                ? mockProperties.getSmsNotifyUrl() : vo.getNotifyUrl());
        pushOrder.setParam1(vo.getParam1());
        pushOrder.setParam2(vo.getParam2());
        pushOrder.setExtra(vo.getExtra());
        pushOrder.setStatus(PushOrderStatus.CREATED);
        pushOrder.setOrderType(PushOrderType.SMS);

        return pushOrder;
    }

    private void doQueryBatchDetail(QuerySmsBatchDetailDto dto, List<SmsBatchDetail> records) {
        log.info("【第一步】准备发送异步查询推送结果的MQ dto {} records {}", dto, records);
        if (dto.getQueryType() == SmsQueryType.DETAIL) {
            for (SmsBatchDetail record : records) {
                if (record.getStatus() != SmsBatchDetailStatus.CREATED) {
                    continue;
                }
                QuerySmsPushResultDetailDto querySmsPushResultDetailDto = new QuerySmsPushResultDetailDto();
                querySmsPushResultDetailDto.setOrderId(dto.getOrderId());
                querySmsPushResultDetailDto.setChannel(dto.getChannel());
                querySmsPushResultDetailDto.setBizId(record.getBizId());
                querySmsPushResultDetailDto.setParams(dto.getParams());
                querySmsPushResultDetailDto.setBatchNo(dto.getBatchNo());
                querySmsPushResultDetailDto.setPhoneNumber(record.getPhoneNumber());
                querySmsPushResultDetailDto.setPushTime(dto.getPushTime());
                // 这里开始查询推送情况
                log.info("【第一步】准备发送异步查询推送结果的MQ（明细方式） {}",
                    querySmsPushResultDetailDto);

                mqProducer.sendDelayMessage(MqConstants.QUEUE_QUERY_SMS_PUSH_RESULT_DETAIL,
                    querySmsPushResultDetailDto,
                    MqConstants.SMS_ASYNC_QUERY_RETRY_SECONDS[querySmsPushResultDetailDto.getCurIndex()]
                        * 1000L);
            }
        } else if (dto.getQueryType() == SmsQueryType.PAGE) {
            QuerySmsPushResultPageDto querySmsPushResultPageDto = new QuerySmsPushResultPageDto();
            querySmsPushResultPageDto.setOrderId(dto.getOrderId());
            querySmsPushResultPageDto.setChannel(dto.getChannel());
            querySmsPushResultPageDto.setBizId(dto.getBizId());
            querySmsPushResultPageDto.setParams(dto.getParams());
            querySmsPushResultPageDto.setBatchNo(dto.getBatchNo());
            querySmsPushResultPageDto.setPushTime(dto.getPushTime());
            querySmsPushResultPageDto.setPageIndex(1); // 固定从第一页查询
            // 这里开始查询推送情况
            log.info("【第一步】准备发送异步查询推送结果的MQ（分页方式） {}",
                querySmsPushResultPageDto);

            mqProducer.sendDelayMessage(MqConstants.QUEUE_QUERY_SMS_PUSH_RESULT_PAGE,
                querySmsPushResultPageDto,
                MqConstants.SMS_ASYNC_QUERY_RETRY_SECONDS[querySmsPushResultPageDto.getCurIndex()]
                    * 1000L);
        } else {
            throw new DefaultSysException("不支持的queryType" + dto.getQueryType());
        }
    }
}
